using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;

namespace Framework.DataTable
{
    public class BuiltinProcessor : IDataTableProcessor
    {
        protected string _branchTag = "@";
        protected string _tableClassNameFormat = "{0}Table";
        protected string _tableDataClassNameFormat = "{0}Data";
        protected string _manifest = "manifest.txt";
        protected string _managerClassName = "TableManager";
        private Dictionary<string, DataParserConfig> _dataParserConfig = new Dictionary<string, DataParserConfig>();
        private Dictionary<string, string> _typeAlias = new Dictionary<string, string>();
        private Dictionary<string, Type> _dataInstanceType = new Dictionary<string, Type>();
        //
        public virtual string GetWatchDirectory()
        {
            return "DataTable/";
        }
        //必须重载的方法，要不然不能生成数据
        public virtual DataTableManager GetManager()
        {
            return null;
        }
        //添加类型解析器
        protected void AddParser(IDataParser parser)
        {
            var typeName = parser.GetTypeName();
            if (!_dataParserConfig.ContainsKey(typeName))
            {
                var config = new DataParserConfig(parser);
                if (!string.IsNullOrEmpty(config.Error))
                {
                    UnityEngine.Debug.LogError(typeName + " add fail case " + config.Error);

                }
                _dataParserConfig.Add(typeName, config);

            }
        }
        //添加自定义类型
        protected void SetTypeAlias(Type type, params string[] alias)
        {
            foreach (var item in alias)
            {
                if (!_typeAlias.TryGetValue(item, out var value))
                {
                    _typeAlias.Add(item, type.FullName);
                }
                else
                {
                    UnityEngine.Debug.LogError(item + " is an alias of " + value);
                }
            }
        }

        public DataParserConfig GetParser(string type)
        {
            if (_dataParserConfig.TryGetValue(GetFieldType(type), out var config))
            {
                return config;
            }
            return null;
        }
        //获取字段类型
        public string GetFieldType(string type)
        {
            if (_typeAlias.TryGetValue(type, out var value))
            {
                return value;
            }
            return type;
        }
        //设置自定义实例类型
        protected void SetDataType(string name, Type type)
        {
            var key = name.ToLower().Trim();
            if (string.IsNullOrEmpty(key))
            {
                return;
            }

            if (!type.IsClass || type.IsAbstract)
            {
                return;
            }
            bool has = false;
            var lst = type.GetInterfaces();
            foreach (var item in lst)
            {
                if (item == typeof(IDataTable))
                {
                    has = true;
                    break;
                }
            }
            if (!has)
            {
                FrameworkLog.LogErrorFormat("{0} must be IDataTable", type.Name);
                return;
            }
            if (!_dataInstanceType.ContainsKey(key))
            {
                _dataInstanceType.Add(key, type);
                return;
            }
            _dataInstanceType[key] = type;
        }

        public virtual FileType GetFileType(string path)
        {
            var fileName = Path.GetFileName(path);
            if (fileName.StartsWith("~"))
            {
                return FileType.Unknow;
            }
            var ext = Path.GetExtension(path);
            if (ext == ".tsv")
            {
                return FileType.Text;
            }
            else if (ext == ".xlsx" || ext == ".xls")
            {
                return FileType.Excel;
            }
            return FileType.Unknow;
        }
        protected virtual string GetTemplatePath()
        {
            return Path.Combine(Path.GetDirectoryName(Utils.File.GetCurrentScriptFilePath()), "Template");
        }
        public virtual void Setup(BuildContext context)
        {
        }

        public virtual void BuildCode(ProcessData data)
        {
            var path = data.Path;
            if (string.IsNullOrEmpty(path))
            {
                return;
            }
            data.Finish = true;
            var tplPath = GetTemplatePath();
            string tplItem = File.ReadAllText(Path.Combine(tplPath, "item.txt"));
            string tplTable = File.ReadAllText(Path.Combine(tplPath, "table.txt"));
            string tplManager = File.ReadAllText(Path.Combine(tplPath, "manager.txt"));
            var NeedCompile = false;
            var define = data.Define;
            string md5;
            string oldMd5;
            foreach (var info in data.ClassInfos)
            {
                var tablefileName = string.Format(_tableClassNameFormat, info.Name);
                var template = new TemplateDataTable
                {
                    contentTable = tplTable,
                    contentItem = tplItem
                };
                template.SetDefine(define);
                template.SetClassInfo(info);
                template.SetItemClassName(string.Format(_tableDataClassNameFormat, info.Name));
                template.SetTableClassName(tablefileName);
                template.SetParser(this);
                if (_dataInstanceType.TryGetValue(info.Name.ToLower(), out var instanceType))
                {
                    template.SetItemInstanceClassName(instanceType.Name);
                }

                var tableFilePath = Path.Combine(path, string.Format("{0}.cs", tablefileName));
                if (!Directory.Exists(Path.GetDirectoryName(tableFilePath)))
                {
                    Directory.CreateDirectory(Path.GetDirectoryName(tableFilePath));
                }
                var tableContent = template.GenCode();
                if (string.IsNullOrEmpty(tableContent))
                {
                    return;
                }
                if (!NeedCompile)
                {
                    md5 = Utils.File.GetMD5(tableContent);
                    if (File.Exists(tableFilePath))
                    {
                        oldMd5 = Utils.File.GetMD5(File.ReadAllBytes(tableFilePath));
                        if (md5 != oldMd5)
                        {
                            NeedCompile = true;
                        }
                    }
                }
                File.WriteAllText(tableFilePath, tableContent);
            }
            var manager = new TemplateDataTableManager
            {
                contentManager = tplManager
            };
            manager.SetClassName(_managerClassName);
            foreach (var item in data.Tables)
            {
                manager.AddItem(item, string.Format(_tableClassNameFormat, item));
            }
            var managerPath = Path.Combine(path, _managerClassName + ".cs");
            var managerContent = manager.Process();
            md5 = Utils.File.GetMD5(managerContent);
            if (File.Exists(managerPath))
            {
                oldMd5 = Utils.File.GetMD5(File.ReadAllBytes(managerPath));
                if (md5 != oldMd5)
                {
                    NeedCompile = true;
                }
            }
            File.WriteAllText(managerPath, managerContent);
            if (NeedCompile)
            {
                AssetDatabase.Refresh();
                UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
            }
        }
        public virtual void BuildData(ProcessData data)
        {
            var path = data.Path;
            if (string.IsNullOrEmpty(path))
            {
                return;
            }
            var list = data.DataInfos;
            var manager = GetManager();
            if (manager == null)
            {
                return;
            }
            data.Finish = true;
            var map = new Dictionary<string, IDataTableLoader>();
            for (int i = 0; i < manager.Count; i++)
            {
                var loader = manager.GetLoader(i);
                map.Add(loader.GetTableName(), loader);
            }
            var buffer = new ByteArray(1024 * 1024 * 10);
            foreach (var item in list)
            {
                // Debug.LogError(item.Name + ":" + item.Branch + ":" + item.Count);
                if (!map.TryGetValue(item.Name, out var loader))
                {
                    continue;
                }
                var name = item.Name;
                if (!string.IsNullOrEmpty(item.Branch))
                {
                    name += _branchTag + item.Branch;
                }
                var bytesPath = Path.Combine(path, name + ".bytes");
                loader.EditorClear();
                for (int i = 0; i < item.Count; i++)
                {
                    var line = item.GetLine(i);
                    loader.ParseItem(line);
                }
                try
                {
                    buffer.Clear();
                    loader.Encode(buffer);
                    if (File.Exists(bytesPath))
                    {
                        File.Delete(bytesPath);
                    }
                    Utils.File.WriteAllBytes(bytesPath, buffer.Buffer.Buffer, buffer.Position);
                }
                catch (System.Exception)
                {

                    FrameworkLog.LogErrorFormat("处理数据时出错{0}", item.Name);
                }
                // File.WriteAllText(textPath, builder.ToString());
            }
        }
        public virtual void Cleanup(BuildContext context, ProcessData data)
        {
            if (string.IsNullOrEmpty(data.Path))
            {
                return;
            }
            if (data.ClassInfos.Count > 0)
            {
                //不处理代码文件
                return;
            }
            var map = new Dictionary<string, bool>();
            foreach (var item in context.Tables)
            {
                map.Add(item, true);
            }
            var fileList = Directory.GetFileSystemEntries(data.Path, "*.*");
            var builder = new StringBuilder();
            foreach (var item in fileList)
            {
                var tname = Path.GetFileName(item);
                var ext = Path.GetExtension(item);
                if (ext != ".bytes")
                {
                    continue;
                }
                var sid = tname.IndexOf(".");
                if (sid >= 0)
                {
                    tname = tname.Substring(0, sid);
                }
                sid = tname.IndexOf(_branchTag);
                if (sid >= 0)
                {
                    tname = tname.Substring(0, sid);
                }
                sid = tname.IndexOf(_branchTag);
                if (sid >= 0)
                {
                    tname = tname.Substring(0, sid);
                }
                if (!map.ContainsKey(tname))
                {
                    File.Delete(item);
                }
                else
                {
                    var info = new FileInfo(item);
                    tname = Path.GetFileNameWithoutExtension(item);
                    builder.AppendLine(tname + ":" + info.Length);
                }
            }
            try
            {
                File.WriteAllText(Path.Combine(data.Path, _manifest), builder.ToString().Trim());
            }
            catch (System.Exception e)
            {
                context.LogError(e.ToString());
            }
        }
    }
}